<?php
/**
 * Created by PhpStorm.
 * User: Rain
 * Date: 2019/11/29
 * Time: 11:19
 */

namespace app\service\alone\File;

use app\admin\service\BaseService;
use app\service\interfaces\FileHandlerInterface;
use think\facade\Filesystem;
use think\Response;

class FileService extends BaseService implements FileHandlerInterface
{
    /**
     * 临时目录文件复制到真实目录文件
     *
     * @param $path
     * @return array
     */
    public function cp(string $path): array
    {
        $location = config('filesystem.location');

        if (!is_file($oldPath = Filesystem::disk('public')->path($path))) return echoArr(500, '资源不存在');

        $fileArr = count(explode(DIRECTORY_SEPARATOR, $path)) > 1 ? explode(DIRECTORY_SEPARATOR, $path) : explode('/', $path);
        if ($fileArr[0] != $location['tmp']) return echoArr(200, '无需复制', ['path' => $path]);

        array_shift($fileArr);

        // 新路径
        $newPath = $location['real'] . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $fileArr);

        if (is_file($movePath = Filesystem::disk('public')->path($newPath))) return echoArr(200, '资源已存在', ['path' => $newPath]);

        try {
            // 目录不存在则创建
            if (!is_dir($dir = pathinfo($movePath)['dirname'])) {
                if (!mkdir($dir, 0755, true)) return echoArr(500, '权限不足');
            }

            // 移动文件
            if (!copy($oldPath, $movePath)) return echoArr(500, '复制文件失败');

            return echoArr(200, '请求成功', [
                'path' => $newPath
            ]);
        } catch (\Exception $exception) {
            return echoArr(500, $exception->getMessage());
        }
    }

    /**
     * 验证资源是否存在
     *
     * @param string $path
     * @return array
     */
    public function exists(string $path): array
    {
        if (!is_file($oldPath = Filesystem::disk('public')->path($path))) return echoArr(500);

        return echoArr(200, '资源存在');
    }

    /**
     * 输入文件流
     *
     * @param string $suffix
     * @param int $type
     * @param string $content
     * @return array
     * @throws \Exception
     */
    public function put(string $suffix, int $type, string $content): array
    {
        $location = config('filesystem.location');

        switch ($type) {
            case 1:
                $category = 'img';
                break;
            case 2:
                $category = 'video';
                break;
            default:
                $category = $location['temp'];
        }

        $path = $location['real'] . DIRECTORY_SEPARATOR . $category . DIRECTORY_SEPARATOR . date('Y-m-d') . DIRECTORY_SEPARATOR;

        $rand = sha1(randomNum(random_int(11111, 999999))) . ".$suffix";

        if (!Storage::disk('public')->put($path . $rand, $content)) return echoArr(302);

        return echoArr(200, '存储成功', [
            'path' => $path . $rand
        ]);
    }

    /**
     * 输出文件流
     *
     * @param string $path
     * @param bool $isDownload
     * @param string $name
     * @param bool $simple
     *
     * @return mixed|\think\response\File
     */
    public function out(string $path, bool $isDownload, string $name = '', bool $simple = true): Response
    {
        // 文件下载或极简查看
        if ($isDownload || $simple) return download($path, $name)->force($isDownload);

        // 通过索引查看
        [
            'content' => $content,
            'index' => ['start' => $start, 'end' => $end],
            'size' => $size
        ] = $this->getOutData(
            $path,
            (int)explode('-', explode('=', request()->header('Range', 'bytes=0-'))[1])[0],
            2 * 1024 * 1024
        );

        // 打开文件
        return Response::create($content, 'file', 206)
            ->header([
                'Accept-Ranges' => 'bytes',
                'Content-Range' => "bytes $start-$end/$size"
            ])
            ->isContent(true)
            ->mimeType(finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path))
            ->name($name ? ($name . pathinfo($path, PATHINFO_EXTENSION)) : pathinfo($path, PATHINFO_BASENAME), false)
            ->force(false);
    }

    /**
     * 通过指针输出文件流
     *
     * @param string $path 文件路径
     * @param int $start 开始指针
     * @param int $step 读取字节
     * @return array
     */
    private function getOutData(string $path, int $start, int $step): array
    {
        $size = filesize($path);
        $end = $start + $step - 1 > $size - 1 ? $size - 1 : $start + $step - 1;

        $file = fopen($path, 'r');
        fseek($file, $start);
        $content = fread($file, $end - $start + 1);
        fclose($file);

        return [
            'content' => $content,
            'index' => ['start' => $start, 'end' => $end],
            'size' => $size
        ];
    }
}
